package sweet.fm

import freemarker.ext.beans.{ BeansWrapper }
import freemarker.ext.util.{ ModelCache, ModelFactory }
import freemarker.template.{ ObjectWrapper, SimpleObjectWrapper, TemplateHashModel, TemplateModel, 
  TemplateException, TemplateModelException }
import sweet.helper.{ JMap, JList, JCollection }
import scala.collection.{ mutable }
import java.{ util=>jutil }

/** A implemenation of BeansWrapper that support native Scala basic and collection types
 * in Freemarker template engine. 
 * 
 * Extends from SimpleObjectWrapper instead of ObjectWrapper because we don't need default
 * not found types to create Jython implementation (which ObjectWrapper will do).
 */
class ScalaBeansWrapper extends SimpleObjectWrapper { 
  override def wrap(obj: Object): TemplateModel = {
    obj match {
      case smap: scala.collection.Map[_,_] => super.wrap(JMap(smap))
      case sseq: scala.Seq[_] => super.wrap(new JList(sseq))
      case sitr: scala.Iterable[_] => super.wrap(JCollection(sitr))
      case sobj: ScalaObject => new ScalaHashModel(this, sobj)
      case _ => super.wrap(obj)
    }     
  }
}

/** A model that will expose all Scala getters that has zero parameters
 * to the FM Hash#get method so can retrieve it without calling with parenthesis. */
class ScalaHashModel(wrapper: ObjectWrapper, sobj: ScalaObject) extends TemplateHashModel{
  type Getter = () => AnyRef
  
  val gettersCache = new mutable.HashMap[Class[_], mutable.HashMap[String, Getter]] 
  
  val getters = {
    val cls = sobj.getClass
    gettersCache.synchronized{
      gettersCache.get(cls) match {
        case Some(cachedGetters) => cachedGetters
        case None =>{
          val map = new mutable.HashMap[String, Getter]
          cls.getMethods.foreach { m =>
            val n = m.getName
            if(!n.endsWith("_$eq") && m.getParameterTypes.length==0){
              map += Pair(n, (() => m.invoke(sobj, Array())))
            }
          }
          gettersCache.put(cls, map)
          map
        }
      }
    }
  }
  def get(key: String) : TemplateModel = getters.get(key) match {
    case Some(getter) => wrapper.wrap(getter())
    case None => throw new TemplateModelException(key+" not found in object "+sobj)
  }
  def isEmpty = false
}

